package cn.fetosoft.woodpecker.core.data.base;

import cn.fetosoft.woodpecker.core.annotation.NotBlank;
import cn.fetosoft.woodpecker.core.exceptions.ValidatorException;
import cn.fetosoft.woodpecker.core.util.BeanUtil;
import com.mongodb.BasicDBObject;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import java.lang.reflect.Field;
import java.util.List;

/**
 * Mongodb CRUD基类
 * @author guobingbing
 * @create 2020-02-27 11:08
 */
public abstract class AbstractMongoService<T extends BaseEntity, P extends BaseForm> implements BaseDataService<T, P> {

	@Autowired
	private MongoTemplate mongoTemplate;

	/**
	 * 返回MongoTemplate
	 * @author guobingbing
	 * @date 2016/12/6
	 * @return
	 */
	protected MongoTemplate getMongoTemplate(){
		return mongoTemplate;
	}

	/**
	 * 生成列表查询字段
	 * @author guobingbing
	 * @date 2020/12/3 15:26
	 * @param
	 * @return org.bson.Document
	 */
	private BasicDBObject genListFields(T t){
		BasicDBObject dbObject = new BasicDBObject();
		List<java.lang.reflect.Field> fields = BeanUtil.getDeclaredFields(t.getClass());
		this.addDbObject(dbObject, fields);
		return dbObject;
	}

	private void addDbObject(BasicDBObject dbObject, List<java.lang.reflect.Field> fields){
		for(java.lang.reflect.Field f : fields){
			f.setAccessible(true);
			DataField dataField = f.getAnnotation(DataField.class);
			if(dataField!=null && dataField.listable()){
				if (StringUtils.isNotBlank(dataField.fieldName())) {
					dbObject.put(dataField.fieldName(), 1);
				}else{
					dbObject.put(f.getName(), 1);
				}
			}
		}
	}

	/**
	 * 将当前对象转换成Docuemnt
	 * @author guobingbing
	 * @date 2020/12/1 11:28
	 * @param
	 * @return org.bson.Document
	 */
	private Update buildUpdate(T t) throws Exception{
		Update update = new Update();
		List<java.lang.reflect.Field> fields = BeanUtil.getDeclaredFields(t.getClass());
		this.addUpdateFiled(update, t, fields);
		return update;
	}

	private void addUpdateFiled(Update update, T t, List<java.lang.reflect.Field> fields) throws Exception{
		for(java.lang.reflect.Field f : fields){
			f.setAccessible(true);
			DataField dataField = f.getAnnotation(DataField.class);
			if(dataField!=null && dataField.updatable()){
				Object fv = f.get(t);
				if(fv!=null) {
					if (StringUtils.isNotBlank(dataField.fieldName())) {
						update.set(dataField.fieldName(), fv);
					}else{
						update.set(f.getName(), fv);
					}
				}
			}
		}
	}

	/**
	 * 通过ID查询
	 * @param id
	 * @return
	 */
	@Override
	public T findById(String id, Class<T> clazz){
		return this.getMongoTemplate().findById(new ObjectId(id), clazz);
	}

	/**
	 * 分页查询
	 *
	 * @param form
	 * @return java.util.List<T>
	 * @author guobingbing
	 * @date 2020/12/1 17:40
	 */
	@Override
	public List<T> selectListByForm(P form, Class<T> clazz) throws Exception {
		Query query = form.buildQuery();
		//BasicDBObject dbObject = clazz.newInstance().genListFields();
		//BasicQuery basicQuery = new BasicQuery(query.getQueryObject(), Document.parse(dbObject.toJson()));
		BasicQuery basicQuery = new BasicQuery(query.getQueryObject());
		basicQuery.setSortObject(query.getSortObject());
		basicQuery.skip(form.getStartRecord()).limit(form.getRows());
		return this.getMongoTemplate().find(basicQuery, clazz);
	}

	/**
	 * 查询总记录数
	 * @param form
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	@Override
	public long selectCountByForm(P form, Class<T> clazz) throws Exception{
		Query query = form.buildQuery();
		return this.getMongoTemplate().count(query, clazz);
	}

	/**
	 * 新增记录
	 * @param t
	 */
	@Override
	public T insert(T t) throws Exception{
		List<java.lang.reflect.Field> fields = BeanUtil.getDeclaredFields(t.getClass());
		for(Field f : fields){
			this.validator(f, t);
		}
		t.setCreateTime(System.currentTimeMillis());
		return this.getMongoTemplate().insert(t);
	}

	/**
	 * 验证字段规则
	 * @param f
	 * @param t
	 * @throws Exception
	 */
	private void validator(Field f, Object t) throws Exception{
		NotBlank notBlank = f.getAnnotation(NotBlank.class);
		if(notBlank!=null){
			f.setAccessible(true);
			Object value = f.get(t);
			if(value==null || StringUtils.isBlank(value.toString())){
				throw new ValidatorException(notBlank.message());
			}
		}
	}

	/**
	 * 变更文档
	 * @param t
	 * @return
	 * @throws Exception
	 */
	@Override
	public UpdateResult update(T t) throws Exception{
		Query query = new Query();
		query.addCriteria(Criteria.where("_id").is(new ObjectId(t.getId())));
		t.setModifyTime(System.currentTimeMillis());
		Update update = this.buildUpdate(t);
		return this.getMongoTemplate().updateFirst(query, update, t.getClass());
	}

	/**
	 * 通过ID删除文档
	 * @param id
	 * @param clazz
	 * @return
	 */
	@Override
	public DeleteResult deleteById(String id, Class<T> clazz){
		Query query = new Query();
		query.addCriteria(Criteria.where("_id").is(new ObjectId(id)));
		return this.getMongoTemplate().remove(query, clazz);
	}

	/**
	 * 根据多个条件删除
	 * @author guobingbing
	 * @wechat t_gbinb
	 * @date 2021/8/7 11:34
	 * @param form
	 * @param clazz
	 * @return com.mongodb.client.result.DeleteResult
	 * @version 1.0
	 */
	@Override
	public DeleteResult deleteByForm(BaseForm form, Class<T> clazz) throws Exception{
		Query query = form.buildQuery();
		return this.getMongoTemplate().remove(query, clazz);
	}

	/**
	 * 原子增或减，当为负值时则减
	 * @param id
	 * @param fieldName 字段名称
	 * @param value 数值，正值为增，负值为减
	 * @param clazz
	 * @return com.mongodb.client.result.UpdateResult
	 */
	protected UpdateResult atomicInc(String id, String fieldName, int value, Class<T> clazz){
		Update update = new Update().inc(fieldName, value);
		Query query = new Query(Criteria.where("id").is(new ObjectId(id)));
		return this.getMongoTemplate().updateFirst(query, update, clazz);
	}

	/**
	 * 向文档的数组中新增一个元素
	 * @author guobingbing
	 * @date 2020/12/8 9:18
	 * @param id
	 * @param key
	 * @param value
	 * @param clazz
	 * @return com.mongodb.client.result.UpdateResult
	 */
	protected UpdateResult push(String id, String key, Object value, Class<T> clazz){
		Query query = new Query(Criteria.where("id").is(new ObjectId(id)));
		Update update = new Update().push(key, value);
		return this.getMongoTemplate().updateFirst(query, update, clazz);
	}

	/**
	 * 删除数组中的一个元素
	 * @author guobingbing 
	 * @date 2020/12/8 9:43
	 * @param id
	 * @param key
	 * @param position LAST-表示最后一个元素, FIRST-表示第一个元素
	 * @param clazz
	 * @return com.mongodb.client.result.UpdateResult		
	 */	
	protected UpdateResult pop(String id, String key, Update.Position position, Class<T> clazz){
		Query query = new Query(Criteria.where("id").is(new ObjectId(id)));
		Update update = new Update().pop(key, position);
		return this.getMongoTemplate().updateFirst(query, update, clazz);
	}
}
